/**
 *  @file Node.cpp
 */
#include "Node.h"
#include "../../common/CppMemPool.h"
#include "../../common/UtilFuncT.h"
#include "DOMException.h"

namespace cppflib
{

namespace xml
{

namespace dom
{

Node::Node(void)
{
}

Node::Node(CFString *pKey) : MCTreeNode(pKey)
{
   this->pTextContent = new CFString();
   cppmempool::Retain(this->pTextContent);
   this->pAttrs = NULL;
}

Node::~Node(void)
{
   if (pTextContent)
      cppmempool::Release(pTextContent);
   if (pAttrs)
      cppmempool::Release(pAttrs);
}

bool Node::AddChild(MCTreeNode * pChild)
{
   if (cppflib::InstanceOf<Node>(pChild)) {
      return MCTreeNode::AddChild(pChild);
   }

   throw DOMException(_S("argument is not an instance of xml::dom::Node"));
}

bool Node::DeleteChild(const BaseObject &key)
{
   if (cppflib::InstanceOf<CFString>(&key)) {
      return MCTreeNode::DeleteChild(key);
   }

   throw DOMException(_S("key is not an instance of CFString"));
}

bool Node::IsAncestor(MCTreeNode *pNode)
{
   if (cppflib::InstanceOf<Node>(pNode)) {
      return MCTreeNode::IsAncestor(pNode);
   }

   throw DOMException(_S("argument is not an instance of xml::dom::Node"));
}

bool Node::Move(MCTreeNode *pNode)
{
   if (cppflib::InstanceOf<Node>(pNode)) {
      return MCTreeNode::Move(pNode);
   }

   throw DOMException(_S("argument is not an instance of xml::dom::Node"));
}

void Node::DetachFromTree()
{
   throw DOMException(_S("Not implemented by xml::dom::Node"));
}

/**
 *  Return the attribute list
 */
const collections::LinkedHashtableT<CFString, CFString> * Node::GetAttributes()
{
   return pAttrs;
}

/**
 *  Assign attribute list to this node. (Content of attributes are just shallow copy)
 */
void Node::SetAttributes(const collections::LinkedHashtableT<CFString, CFString> & attributes)
{
   if (pAttrs) {
      cppmempool::Release(pAttrs);
      pAttrs = NULL;
   }

   if (attributes.GetCount() > 0) {
      pAttrs = attributes.Copy();
      cppmempool::Retain(pAttrs);
   }
}

/**
 *  Node name
 */
const CFString * Node::GetNodeName()
{
   return static_cast<const CFString*>(GetKey());
}

/**
 *  Text content (may not present)
 */
const CFString * Node::GetNodeTextContent()
{
   return pTextContent;
}

void Node::AppendTextContent(const CFString &text)
{
   *pTextContent += text;
}

bool Node::HasAttributes()
{
   return (pAttrs != NULL && pAttrs->GetCount() > 0);
}

bool Node::HasChildNodes()
{
   return (this->GetDirectChildCount() > 0);
}

/**
 *  Get a child node by a XML tag name
 *
 *  @param [in] tagName -- can be 
 *                             i) a single tag name or
 *                             ii) path to a nested tag node
 *                                 (e.g. "tag1/tag2/tag3/targetTag")
 *  @return the first matched node (if more than one) or null if not found
 */
Node * Node::GetChildNodeByTagName(pcwstr_t tagName)
{
   int start = 0;
   CFString orgTag(tagName);
   orgTag.Trim();
   Node * pNode = this;

   while (pNode != NULL && start >= 0) {
      if (!pNode->HasChildNodes()) {
         pNode = NULL;
         break;
      }

      CFString tempTag = orgTag.Tokenize(_S("/"), start);
      tempTag.Trim();
      if (tempTag.IsEmpty()) { // shouldn't be empty
         pNode = NULL;
         break;
      }
      
      // find matched child node
      Node *pFoundNode = NULL;
      for (u32_t i = 0; i < pNode->GetDirectChildCount(); ++i) {
         Node *pChild = static_cast<Node*>(pNode->GetChild(i));
         if (*pChild->GetNodeName() == tempTag) {
            pFoundNode = pChild;
            break;
         }
      }

      pNode = pFoundNode; // may be NULL
   }

   return pNode;
}

} // end of namespace dom

} // end of namespace xml

} // end of namespace cppflib

